package base;

import static base.VertexState.OFF;
import static base.VertexState.ON;
import static base.VertexState.both;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;

import collect.Range;

import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

public class SupportState implements advanced.SupportState<Integer, VertexState> {
	
	private final ImmutableList<VertexState> asList;
	public List<VertexState> asList() { return asList; }
	
	//TODO fix ImmutableSortedSetMultimap implementation and use that
	private final ImmutableMap<VertexState,ImmutableSortedSet<Integer>> state;
	private final String out;
	public SupportState(List<VertexState> states) {
		asList = ImmutableList.copyOf(states);
		StringBuilder outBuilder = new StringBuilder();
		Map<VertexState, ImmutableSortedSet.Builder<Integer>> stateBuilder = foundation();
		for (Integer vertex=0; vertex<states.size(); vertex++) {
			stateBuilder.get(states.get(vertex)).add(vertex);
			outBuilder.append(states.get(vertex) == ON ? 1 : 0).append(" ");
		}
		state = compile(stateBuilder);
		out = outBuilder.substring(0, outBuilder.length()-1).toString();
	}
	
	public SupportState(boolean...states) {
		StringBuilder outBuilder = new StringBuilder();
		Map<VertexState, ImmutableSortedSet.Builder<Integer>> stateBuilder = foundation();
		ImmutableList.Builder<VertexState> asListB = ImmutableList.builder();
		Integer vertex = 0;
		for (boolean b : states) {
			stateBuilder.get(b ? ON:OFF).add(vertex++);
			outBuilder.append(b ? 1 : 0).append(" ");
			asListB.add(b ? ON : OFF);
		}
		state = compile(stateBuilder);
		asList = asListB.build();
		if (states.length!=0) {
			out = outBuilder.substring(0, outBuilder.length()-1).toString();
		} else out="";
	}
	
//	public SupportState(ImmutableMap<VertexState,ImmutableSortedSet<Integer>> state) {
//		this.state = state;
//	}
	
	private static ImmutableMap<VertexState,ImmutableSortedSet<Integer>> compile(Map<VertexState,ImmutableSortedSet.Builder<Integer>> source) {
		ImmutableMap.Builder<VertexState, ImmutableSortedSet<Integer>> builder = ImmutableMap.builder();
		for (VertexState vs : both) builder.put(vs, source.get(vs).build());
		return builder.build();
	};
	
	private static Map<VertexState, ImmutableSortedSet.Builder<Integer>> foundation() {
		Map<VertexState, ImmutableSortedSet.Builder<Integer>> foundation = Maps.newEnumMap(VertexState.class);
		for (VertexState vs : both) foundation.put(vs, ImmutableSortedSet.<Integer>naturalOrder());
		return foundation;
	}
	
	@Override public boolean equals(Object other) {	
		return	(this == other)						? true	: //obviously true
			  	!(other instanceof SupportState) 	? false : //obviously false
			  	SupportState.class.cast(other).state.equals(this.state); //non-obvious
	}
	
	@Override public int hashCode() { return state.hashCode(); }
	
	public SortedSet<Integer> get(VertexState which) {
		return state.get(which);
	}
	
	public Set<Integer> get() {
		return Sets.union(state.get(ON), state.get(OFF));
	}
		
	public static Iterable<SupportState> allOfSize(final int size) {
		return Iterables.transform(Sets.cartesianProduct(Collections.nCopies(size, both)), constructor);
	}
	
	private final static Function<List<VertexState>,SupportState> constructor = new Function<List<VertexState>,SupportState>() {
		@Override public SupportState apply(List<VertexState> from) {
			return !from.contains(ON) ? Zero.make(from.size()) : new SupportState(from);
		}
	};
	
	@Override public String toString() {
		return out;
	}
	
	public static class Zero extends SupportState {
		
//		private static final Map<Integer,Zero> cache = Maps.newHashMap();
		
		public static Zero make(int size) {
			if (size <=0) throw new IllegalArgumentException();
//			if (!cache.containsKey(size)) cache.put(size, new Zero(size));
			return new Zero(size);
		}
		
		private Zero(int size) {
			super();
			vertices = Range.range(size-1);
			copy = Collections.nCopies(size, OFF);
		}
		
		final SortedSet<Integer> vertices;

		@Override public SortedSet<Integer> get(VertexState state) {
			switch (state) {
				case OFF : return get();
				default : return ImmutableSortedSet.of();
			}
		}

		@Override public SortedSet<Integer> get() { return vertices; }
		
		@Override public String toString() {
			return "zero state, size "+vertices.size();
		}

		private final List<VertexState> copy;
		@Override public List<VertexState> asList() {
			return copy;
		}
		
	}
	
}
